package dd.lo.chapter2;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.VideoWriter;
import org.opencv.videoio.Videoio;

import java.io.ByteArrayInputStream;
import java.util.Date;

/**
 * 对应程序示例
 * Example 2-11. A complete program to read in a color video and write out the log-polar-transformed video
 */
public class Example7 extends Application {
    private VideoCapture cap;
    private VideoWriter writer;
    private ImageView imgView;
    private volatile boolean run = true;
    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        launch(args);
    }

    @Override
    public void stop() throws Exception {
        System.out.println("Exiting application...");
        if (null != cap) {
            cap.release();
        }
        if (null != writer) {
            //必须要调用，这样可以把缓冲区的剩余数据全刷到硬盘上
            writer.release();
        }
        super.stop();
    }

    @Override
    public void start(Stage stage) {
        run = true;
        //打开视频文件
        cap = new VideoCapture("/Users/kwok/Downloads/young_women_listening_to_music.mp4");
        //读取视频帧的宽高，用于决定窗口的大小
        int fps = (int) cap.get(Videoio.CAP_PROP_FPS);
        int w = (int) cap.get(Videoio.CAP_PROP_FRAME_WIDTH);
        int h = (int) cap.get(Videoio.CAP_PROP_FRAME_HEIGHT);
        System.out.printf("Video resolution (%d, %d), fps: %d\n", w, h, fps);
        //设置输出文件位置
        writer = new VideoWriter("/Users/kwok/Downloads/log_polar_out.avi", VideoWriter.fourcc('M', 'J', 'P', 'G'), cap.get(Videoio.CAP_PROP_FPS), new Size(w, h));
        //提供一个退出的按钮
        Button exitBtn = new Button("Exit");
        exitBtn.setOnMouseClicked(mouseEvent -> {
            //设置播放状态为false
            run = false;
            try {
                //需要停几十毫秒，以便从摄像头捕获图像的线程能够跑完一个循环并感知到run状态的变化
                Thread.sleep(50L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //然后再执行整个程序的退出
            Platform.exit();
        });
        //将整个窗口最下面的一行作为按钮和文字信息放置的位置
        FlowPane bottomPane = new FlowPane();
        bottomPane.getChildren().add(exitBtn);
        imgView = new ImageView();
        //整个窗口放置一个画面和最下面一行放按钮和文字信息（50个像素高）
        BorderPane root = new BorderPane();
        root.setCenter(imgView);
        root.setBottom(bottomPane);
        Scene scene = new Scene(root, w / 2.0, h / 2.0 + 50);
        stage.setScene(scene);
        stage.setTitle("Example7-Log-polar-transformed video");
        stage.setResizable(false);
        //窗口的元素已准备好，开启图像转换线程
        startTransformation();
        //已就绪，展示主窗口
        stage.show();
    }

    private void startTransformation() {
        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() {
                for (;;) {
                    if (!run) break;
                    //下面也是读帧、处理帧、显示帧的过程
                    Mat frame = new Mat();
                    cap.read(frame);
                    if (frame.empty()) {
                        System.out.println("No image captured, exiting...");
                        break;
                    }
                    //图像扭曲功能，书中例子提供参数
                    Mat logPolarF = new Mat();
                    Imgproc.warpPolar(frame, logPolarF, new Size(frame.width(), frame.height()), new Point(frame.width() / 2.0, frame.height() / 2.0), 40, Imgproc.WARP_FILL_OUTLIERS);
                    //输出到文件中
                    writer.write(logPolarF);
                    //经过调试，实际图像比较大，会阻碍按钮显示，这里将图像压缩一下再显示
                    Imgproc.resize(logPolarF, logPolarF, new Size(logPolarF.width() / 2.0, logPolarF.height() / 2.0));
                    MatOfByte buffer = new MatOfByte();
                    Imgcodecs.imencode(".jpeg", logPolarF, buffer);
                    //显示帧必须放到Platform.runLater中执行，因为这里不是主线程
                    Platform.runLater(() -> imgView.setImage(new Image(new ByteArrayInputStream(buffer.toArray()))));
                }
                return null;
            }
        };
        //记得将task放到线程中并执行
        new Thread(task).start();
    }
}
